package com.hyhy.hycloud.uaa.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.hyhy.hycloud.uaa.SecurityProperties;
import com.hyhy.hycloud.uaa.config.security.LoginFailureHandler;
import com.hyhy.hycloud.uaa.config.security.LoginSuccessHandler;
import com.hyhy.hycloud.uaa.config.security.LogoutSuccessHandler;
import com.hyhy.hycloud.uaa.config.security.UnauthorizedEntryPoint;
import com.hyhy.hycloud.uaa.security.LoginRequestDetailsSource;
import com.hyhy.hycloud.uaa.security.crypto.Md5PasswordEncoder;
import com.hyhy.hycloud.uaa.security.provider.*;
import com.hyhy.hycloud.uaa.service.LoginAttemptService;
import com.hyhy.hycloud.uaa.service.UserService;
import com.hyhy.hycloud.uaa.service.VerificationCodeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.security.web.savedrequest.NullRequestCache;
import org.springframework.session.security.web.authentication.SpringSessionRememberMeServices;
import org.springframework.web.cors.CorsUtils;

/**
 * Spring Security配置
 *
 * @author Xuegui Yuan
 * @date 2018-09-20
 */
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig {

    @Autowired
    UserService userService;

    @Autowired
    VerificationCodeService verificationCodeService;

    private PasswordEncoder passwordEncoder = new Md5PasswordEncoder();

//
//    @Bean
//    public AuthenticationProvider usernamePasswordAuthenticationProvider() {
//        return UsernamePasswordAuthenticationProvider.builder()
//                .appIdConfigFinder(userService)
//                .passwordEncoder(passwordEncoder)
//                .userDetailsByUsernameFinder(userService)
//                .smsVerificationCodeByMobileFinder(verificationCodeService)
//                .build();
//    }

    @Bean
    public AuthenticationProvider mobilePasswordAuthenticationProvider() {
        return MobilePasswordAuthenticationProvider.builder()
                .appIdConfigFinder(userService)
                .passwordEncoder(passwordEncoder)
                .userDetailsByMobileFinder(userService)
                .smsVerificationCodeByMobileFinder(verificationCodeService)
                .build();
    }

    @Bean
    public AuthenticationProvider smsVerificationCodeAuthenticationProvider() {
        return SmsVerificationCodeAuthenticationProvider.builder()
                .appIdConfigFinder(userService)
                .userDetailsByMobileFinder(userService)
                .userRegister(userService)
                .smsVerificationCodeByMobileFinder(verificationCodeService)
                .build();
    }

    @Configuration
    public class LoginConfig extends WebSecurityConfigurerAdapter {

        @Autowired
        SecurityProperties securityProperties;

        @Override
        public void configure(WebSecurity web) {
            web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            // @formatter:off
            ObjectMapper objectMapper = new ObjectMapper();
            http.cors().and().csrf().disable()
                    .authorizeRequests()
                    .antMatchers(HttpMethod.OPTIONS, "/login").permitAll()
                    .requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
                    .antMatchers("/users/user-info", "/session/**").authenticated()
                    .and()
                    .requestCache()
                    .requestCache(new NullRequestCache())
                    .and()
                    .formLogin()
                    .authenticationDetailsSource(new LoginRequestDetailsSource())
                    .successHandler(new LoginSuccessHandler(objectMapper, securityProperties.getMaxInactiveInterval()))
                    .failureHandler(new LoginFailureHandler(objectMapper))
                    .permitAll()
                    .and()
                    .logout()
                    .permitAll()
                    .logoutSuccessHandler(new LogoutSuccessHandler(objectMapper))
                    .invalidateHttpSession(true)
                    .and()
                    .httpBasic()
                    .and()
                    .rememberMe()
                    .rememberMeServices(rememberMeServices())
                    .and()
                    .exceptionHandling()
                    .authenticationEntryPoint(new UnauthorizedEntryPoint(objectMapper));
//                        .and()
//                    .sessionManagement()
//                        .maximumSessions(10)
//                        .maxSessionsPreventsLogin(false);
            // @formatter:on
        }

        @Override
        protected void configure(AuthenticationManagerBuilder auth) {
//            authenticationProvider(usernamePasswordAuthenticationProvider())
            auth.authenticationProvider(mobilePasswordAuthenticationProvider())
                    .authenticationProvider(smsVerificationCodeAuthenticationProvider());
        }

        @Bean
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
        }

        @Bean
        RememberMeServices rememberMeServices() {
            SpringSessionRememberMeServices rememberMeServices =
                    new SpringSessionRememberMeServices();
            // optionally customize
            rememberMeServices.setAlwaysRemember(true);
            return rememberMeServices;
        }
    }

//    @Configuration
//    @EnableAuthorizationServer
//    public class Oauth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
//
//        static final String TOKEN_PREFIX = "hycloud:oauth:";
//
//        @Autowired(required = false)
//        private RedisConnectionFactory redisConnectionFactory;
//
//        @Autowired
//        private AuthenticationManager authenticationManagerBean;
//
//        @Bean
//        TokenStore redisTokenStore() {
//            RedisTokenStore tokenStore = new RedisTokenStore(redisConnectionFactory);
//            tokenStore.setPrefix(TOKEN_PREFIX);
//            return tokenStore;
//        }
//
//        @Override
//        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//            // TODO: 持久化客户端
//            String pwd = "{bcrypt}" + new BCryptPasswordEncoder().encode("app1");
//            clients.inMemory()
//                    .withClient("app1")
//                    .secret(pwd)
//                    .authorizedGrantTypes("client_credentials", "authorization_code", "refresh_token",
//                            "password").scopes("openid");
//        }
//
//        @Override
//        public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
//            oauthServer
//                    .tokenKeyAccess("permitAll()")
//                    .checkTokenAccess("isAuthenticated()")
//                    .allowFormAuthenticationForClients();
//        }
//
//        @Override
//        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
//            endpoints
//                    .tokenStore(redisTokenStore())
//                    .authenticationManager(authenticationManagerBean);
//        }
//
//    }
}
